/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#ifndef SkPngCodec_DEFINED
#define SkPngCodec_DEFINED

#include "include/codec/SkCodec.h"
#include "include/codec/SkEncodedImageFormat.h"
#include "include/core/SkRefCnt.h"
#include "include/private/base/SkTemplates.h"

#include <cstddef>
#include <cstdint>
#include <memory>

class SkColorPalette;
class SkPngChunkReader;
class SkSampler;
class SkStream;
class SkSwizzler;
struct SkEncodedInfo;
struct SkImageInfo;

class SkPngCodec : public SkCodec {
public:
    static bool IsPng(const void *, size_t);

    // Assume IsPng was called and returned true.
    static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result *, SkPngChunkReader * = nullptr);

    // FIXME (scroggo): Temporarily needed by AutoCleanPng.
    void setIdatLength(size_t len)
    {
        fIdatLength = len;
    }

    ~SkPngCodec() override;

protected:
    // We hold the png_ptr and info_ptr as voidp to avoid having to include png.h
    // or forward declare their types here.  voidp auto-casts to the real pointer types.
    struct voidp {
        voidp(void *ptr) : fPtr(ptr) {}

        template <typename T> operator T *() const
        {
            return (T *)fPtr;
        }

        explicit operator bool() const
        {
            return fPtr != nullptr;
        }

        void *fPtr;
    };

    SkPngCodec(SkEncodedInfo &&, std::unique_ptr<SkStream>, SkPngChunkReader *, void *png_ptr, void *info_ptr,
        int bitDepth);

    Result onGetPixels(const SkImageInfo &, void *, size_t, const Options &, int *) override;
    SkEncodedImageFormat onGetEncodedFormat() const override
    {
        return SkEncodedImageFormat::kPNG;
    }
    bool onRewind() override;

    SkSampler *getSampler(bool createIfNecessary) override;
    void applyXformRow(void *dst, const void *src);

    voidp png_ptr()
    {
        return fPng_ptr;
    }
    voidp info_ptr()
    {
        return fInfo_ptr;
    }

    SkSwizzler *swizzler()
    {
        return fSwizzler.get();
    }

    // Initialize variables used by applyXformRow.
    void initializeXformParams();

    /* *
     * Pass available input to libpng to process it.
     *
     * libpng will call any relevant callbacks installed. This will continue decoding
     * until it reaches the end of the file, or until a callback tells libpng to stop.
     */
    bool processData();

    Result onStartIncrementalDecode(const SkImageInfo &dstInfo, void *pixels, size_t rowBytes,
        const SkCodec::Options &) override;
    Result onIncrementalDecode(int *) override;

    sk_sp<SkPngChunkReader> fPngChunkReader;
    voidp fPng_ptr;
    voidp fInfo_ptr;

    // These are stored here so they can be used both by normal decoding and scanline decoding.
    sk_sp<SkColorPalette> fColorTable; // May be unpremul.
    std::unique_ptr<SkSwizzler> fSwizzler;
    skia_private::AutoTMalloc<uint8_t> fStorage;
    void *fColorXformSrcRow;
    const int fBitDepth;

private:
    enum XformMode {
        // Requires only a swizzle pass.
        kSwizzleOnly_XformMode,

        // Requires only a color xform pass.
        kColorOnly_XformMode,

        // Requires a swizzle and a color xform.
        kSwizzleColor_XformMode,
    };

    bool createColorTable(const SkImageInfo &dstInfo);
    // Helper to set up swizzler, color xforms, and color table. Also calls png_read_update_info.
    SkCodec::Result initializeXforms(const SkImageInfo &dstInfo, const Options &);
    void initializeSwizzler(const SkImageInfo &dstInfo, const Options &, bool skipFormatConversion);
    void allocateStorage(const SkImageInfo &dstInfo);
    void destroyReadStruct();

    virtual Result decodeAllRows(void *dst, size_t rowBytes, int *rowsDecoded) = 0;
    virtual void setRange(int firstRow, int lastRow, void *dst, size_t rowBytes) = 0;
    virtual Result decode(int *rowsDecoded) = 0;

    XformMode fXformMode;
    int fXformWidth;

    size_t fIdatLength;
    bool fDecodedIdat;

    using INHERITED = SkCodec;
};
#endif // SkPngCodec_DEFINED
